#import <math.h>
#import <appkit/workspaceRequest.h>
#import <misckit/MiscString.h>

#import "PSWEnding.h"
#import "PSWUControl.h"
#import "RTFDescription.h"
#import "UndoManager.h"

#import "Tree.h"

//************************************************************************
//  static c functions declaration

// static functions for layouting
static void setRectFromPolygon(NXRect *rect, Polygon *aPol, const NXPoint *ipu, const NXPoint *ipl,
								NXCoord *low, NXCoord *loh);
static NXCoord merge(Polygon *c1,Polygon *c2, NXZone *zone);
static NXCoord offset(NXCoord p1,NXCoord p2,NXCoord a1,NXCoord a2,
                      NXCoord b1,NXCoord b2);
static Polyline *bridge(Polyline *line1,Polyline *line2,NXCoord x1,NXCoord y1,
                       NXCoord x2,NXCoord y2, NXZone *zone);
static Polyline *allocLine(NXCoord aX,NXCoord aY,Polyline *aLink, NXZone *zone);
static Polyline *copyPolyline(Polyline *aSource,Polyline **aTail, NXZone *zone);
static void freePolyline(Polyline *aLine);
static void partialFreePolyline(Polyline *aLine, Polyline *toEnd);

// static functions for calculating path intersection
static BOOL straddles(const NXPoint *p1, const NXPoint *p2, const NXPoint *p3, const NXPoint *p4);
 
// static variables for general use 
static float controlPts[8],controlX,controlY,officialFontSize;
static char controlChars[5];
static char controlPrimChars[5];
static NXAtom fontname;
static ButtonCell *attachCell, *soundCell; 
static Cell *zippedCell;
static NXRect attachCellFrame, soundCellFrame, zippedCellFrame;
static UPath *arrowEnding, *doubleEnding, *circleEnding;
static UPath *parentArrowEnding, *parentDoubleEnding, *parentCircleEnding;

// static variables for diagram2 streaming
#import "DGVariables.h"
 
@interface Tree(PrivateMethods)

- attachParent:(NXCoord)h;
- layoutLeaf;
- (NXCoord)join;
- makePath;
- changeText;
- changePos;
- fillGPathParams;
- calcWidthHeight;
- internalCopyFromZone:(NXZone *)zone parent:aParent;
- calcIntersection:(NXPoint *)ip angle:(float *)alpha toPoint:(const NXPoint *)aPoint;

@end

@implementation Tree

//************************************************************************
// creating and destroying

+ initialize
{
	int i;
	
	#ifndef FILTER
	
	NXRect dummy;
	id dummyId;
	
	#endif
	
	if(self == [Tree class]){
		[Tree setVersion:1];
        fontname = NXUniqueString("ControlPoints");
        PSWDefineFont(fontname);
		officialFontSize = 8.0;
		controlPts[6] = 0;
		controlPts[7] = 0;
		controlChars[4] = 0;
		controlPrimChars[4] = 0;
		for(i = 0;i < 4;i++){
			controlChars[i] = 'a';
			controlPrimChars[i] = 'e';
		}
		
		#ifndef FILTER
		
		dummyId = [NXImage findImageNamed:"NXLinkButton"];
		if([dummyId lockFocus])
			[dummyId unlockFocus];
		dummyId = [NXImage findImageNamed:"NXLinkButtonH"];
		if([dummyId lockFocus])
			[dummyId unlockFocus];
		dummyId = [NXImage findImageNamed:"zipped"];
		if([dummyId lockFocus])
			[dummyId unlockFocus];
		dummyId = [NXImage findImageNamed:"soundMarker"];
		if([dummyId lockFocus])
			[dummyId unlockFocus];
		dummyId = [NXImage findImageNamed:"soundMarkerH"];
		if([dummyId lockFocus])
			[dummyId unlockFocus];
		NXSetRect(&dummy, 0, 0, 100, 100);
		attachCell = [[ButtonCell alloc] initIconCell:"NXLinkButton"];
		[attachCell setAltIcon:"NXLinkButtonH"];
		[attachCell setBordered:NO];
		[attachCell setHighlightsBy:NX_CONTENTS];
		[attachCell setIconPosition:NX_ICONONLY];
		[attachCell calcCellSize:&(attachCellFrame.size) inRect:&dummy];
		zippedCell = [[Cell alloc] initIconCell:"zipped"];
		[zippedCell setBordered:NO];
		[zippedCell calcCellSize:&(zippedCellFrame.size) inRect:&dummy];
		soundCell = [[ButtonCell alloc] initIconCell:"soundMarker"];
		[soundCell setAltIcon:"soundMarkerH"];
		[soundCell setBordered:NO];
		[soundCell setHighlightsBy:NX_CONTENTS];
		[soundCell setIconPosition:NX_ICONONLY];
		[soundCell calcCellSize:&(soundCellFrame.size) inRect:&dummy];
		
		#endif
		 
		arrowEnding = [[UPath alloc] init];
		[arrowEnding moveto:0 :0];
		[arrowEnding rlineto:-10 :5];
		[arrowEnding rlineto:3 :-5];
		[arrowEnding rlineto:-3 :-5];
		[arrowEnding closepath];
		doubleEnding = [[UPath alloc] init];
		[doubleEnding moveto:0 :0];
		[doubleEnding rlineto:-5 :5];
		[doubleEnding rlineto:2 :-5];
		[doubleEnding rlineto:-7 :5];
		[doubleEnding rlineto:2 :-5];
		[doubleEnding rlineto:-2 :-5];
		[doubleEnding rlineto:7 :5];
		[doubleEnding rlineto:-2 :-5];
		[doubleEnding closepath];
		circleEnding = [[UPath alloc] init];
		[circleEnding moveto:0 :0];
		[circleEnding arc:-3 :0 :3 :0 :360];
		parentArrowEnding = [[UPath alloc] init];
		[parentArrowEnding moveto:0 :0];
		[parentArrowEnding rlineto:10 :5];
		[parentArrowEnding rlineto:-3 :-5];
		[parentArrowEnding rlineto:3 :-5];
		[parentArrowEnding closepath];
		parentDoubleEnding = [[UPath alloc] init];
		[parentDoubleEnding moveto:0 :0];
		[parentDoubleEnding rlineto:5 :5];
		[parentDoubleEnding rlineto:-2 :-5];
		[parentDoubleEnding rlineto:7 :5];
		[parentDoubleEnding rlineto:-2 :-5];
		[parentDoubleEnding rlineto:2 :-5];
		[parentDoubleEnding rlineto:-7 :5];
		[parentDoubleEnding rlineto:2 :-5];
		[parentDoubleEnding closepath];
		parentCircleEnding = [[UPath alloc] init];
		[parentCircleEnding moveto:6 :0];
		[parentCircleEnding arc:3 :0 :3 :0 :360];
	}
	return self;
}

- initLabel:(const char *)aLabel props:(Properties *)aProps
{  
   	[super init];
	contour.lower.head = contour.upper.head = NULL;
   	contour.lower.tail = contour.upper.tail = NULL;
	parent = child = sibling = nil;
	delegate = nil;
	hasVerknuepfung = NO;
	verknuepfung = nil;
	hasSound = NO;
	geraeusch = nil;
	offset.x = offset.y = 0.0;
	pos.x = pos.y = 0.0; 
	selected = NO;
	updateLayout = YES;
	if(aLabel)
		textCell = [[TextFieldCell allocFromZone:[self zone]] initTextCell:aLabel];
	else
		textCell = [[TextFieldCell allocFromZone:[self zone]] initTextCell:aProps->defaultNodeName];
	[textCell setBordered:NO];
	[textCell setBackgroundTransparent:YES];
	[textCell setTextColor:aProps->textColor];
	[textCell setAlignment:NX_CENTERED];
	nodeDescription = [[RTFDescription allocFromZone:[self zone]] init];
	zipped = aProps->zipped;		
	border = aProps->border;
	shadow = aProps->shadow;
	fill = aProps->fill;
	outline = aProps->outline;
	linkKind = aProps->linkKind;
	biegFactor = aProps->biegFactor;
	parentDistance = aProps->parentDistance;
	fillColor = aProps->fillColor;
	outlineColor = aProps->outlineColor;
	shadowColor = aProps->shadowColor;
	ending = aProps->ending;
	parentEnding = aProps->parentEnding;
	gpath = [[UPath allocFromZone:[self zone]] init];
	pathKind = aProps->pathKind;
	deltaShadow.x = deltaShadow.y = 3;
	placer.rect[0] = placer.rect[1] = 6;
	placer.roundRect[0] = placer.roundRect[1] = 6;
	placer.roundRect[2] = 3;
	placer.ellipse[0] = 6;
	placer.ellipse[1] = 4;
	placer.rhomb[0] = 12;
	placer.rhomb[1] = 3;
	placer.hexagon[0] = 6;
	placer.hexagon[1] = 6;
	placer.hexagon[2] = 1;
	[self makePath]; 	
   	return self;
}
   
- free
{
	[nodeDescription free];
	if(contour.lower.head)
		freePolyline(contour.lower.head);
	if(contour.upper.head)
		freePolyline(contour.upper.head);
	if(child)
		[child free];
	if(sibling)
		[sibling free];
	[gpath free];
	if(verknuepfung)
		[verknuepfung free];
	if(geraeusch)
		[geraeusch free];
	[textCell free];
   	return [super free];
}

- copyFromZone:(NXZone *)zone
{
   	Tree *theCopy;

   	theCopy = [super copyFromZone:zone];
	theCopy->contour.upper.head = contour.upper.tail = NULL;
	theCopy->contour.lower.head = contour.lower.tail = NULL;
	theCopy->updateLayout = YES;
	theCopy->parent = nil;
	theCopy->sibling = nil;
	if(child)
		theCopy->child = [child internalCopyFromZone:zone parent:theCopy];
	else
		theCopy->child = nil;
	theCopy->selected = NO;
	theCopy->gpath = [gpath copyFromZone:zone];
	theCopy->nodeDescription = [nodeDescription copyFromZone:zone];
	theCopy->delegate = nil;
	if(verknuepfung)
		theCopy->verknuepfung = [verknuepfung copyFromZone:zone];
	else
		theCopy->verknuepfung = nil;
	if(geraeusch)
		theCopy->geraeusch = [geraeusch copyFromZone:zone];
	else
		theCopy->geraeusch = nil;
	theCopy->textCell = [textCell copyFromZone:zone];
   	return theCopy;
}

//************************************************************************
// layout and drawing

- involved:(const NXRect *)aRect addTo:aList link:aPath
{
	NXRect rect,dummyRect;
	NXPoint point,mp,parentPoint,childPoint;
	NXCoord dist;

	if(parent && parent->zipped)
		return self;
	[self getBounds:&rect];
	if(aRect == NULL || NXIntersectsRect(&rect,aRect))
		[aList addObject:self];
	if(parent){
		[parent linkPoint:&point];
		[self linkPoint:&mp];
		if(linkKind){
			[parent linkParentPoint:&parentPoint];
			[self linkChildPoint:&childPoint];
		}	
		dist = ABS(point.y - mp.y);
		if(dist > 0)
			NXSetRect(&dummyRect,MIN(mp.x,point.x) - 0.5,MIN(mp.y,point.y) - 0.5,
					ABS(point.x - mp.x) + 1,dist + 1);
		else
			NXSetRect(&dummyRect,MIN(mp.x,point.x),mp.y - 0.5,
					ABS(point.x - mp.x),1.0);
		if(aRect == NULL || NXIntersectsRect(aRect,&dummyRect)){
			if(linkKind == 0){
				[aPath moveto:point.x :point.y];
				[aPath lineto:mp.x :mp.y];
			} else {
				[aPath moveto:point.x :point.y];
				[aPath lineto:parentPoint.x :parentPoint.y];
				[aPath lineto:childPoint.x :childPoint.y];
				[aPath lineto:mp.x :mp.y];
			}
		}
	}
	if(child)
		[child involved:aRect addTo:aList link:aPath];
	if(sibling)
		[sibling involved:aRect addTo:aList link:aPath];
	return self;
}

- getBounds:(NXRect *)rect
{
	NXRect dummy;
	NXCoord dxy, dwh;
	
	NXSetRect(rect,pos.x,pos.y,width,height);
	if(selected){
		dxy = MAX(5, linewidth);
		dwh = MAX(10, linewidth);	
		rect->origin.x -= dxy;
		rect->origin.y -= dxy;
		rect->size.width += dwh;
		rect->size.height += dwh;
	}
	if(shadow){
		rect->size.width += deltaShadow.x;
		rect->size.height += deltaShadow.y;
	}
	if((ending && parent) || (parentEnding && child)){
		NXInsetRect(rect, -15, -15);
	}
	if(hasVerknuepfung){
		NXSetRect(&dummy, pos.x + 23, 
			pos.y - linewidth - attachCellFrame.size.height - 1, 
			attachCellFrame.size.width, 
			attachCellFrame.size.height);
		NXUnionRect(&dummy, rect);
	}
	if(zipped){
		NXSetRect(&dummy, pos.x + 7, 
			pos.y - linewidth - zippedCellFrame.size.height - 1, 
			zippedCellFrame.size.width, 
			zippedCellFrame.size.height);
		NXUnionRect(&dummy, rect);
	}
	if(hasSound){
		NXSetRect(&dummy, pos.x + 37, 
			pos.y - linewidth - soundCellFrame.size.height - 1, 
			soundCellFrame.size.width, 
			soundCellFrame.size.height);
		NXUnionRect(&dummy, rect);
	}
	return self;
}

- getTreeBounds:(NXRect *)rect lowerWidth:(NXCoord *)lowW lowerHeight:(NXCoord *)lowH
{
	NXPoint p1,p2;
	
	p1.x = pos.x - border;
	p1.y = pos.y - border;
	p2.x = pos.x - border;
	p2.y = pos.y + height + border;
	setRectFromPolygon(rect, &contour, &p1, &p2, lowW, lowH);
	NXInsetRect(rect,border - linewidth,border - linewidth);
	if(shadow){
		rect->size.width += deltaShadow.x;
		rect->size.height += deltaShadow.y;
	}
	return self;
}

- hit:(HitPath *)hitUPath
{
	BOOL didHit;
	id dummy;

	didHit = [hitUPath hitPathFill:gpath];
	if(didHit)
		return self;
	else if(child){
		dummy = [child hit:hitUPath];
		if(dummy)
			return dummy;
		else if(sibling)
			return [sibling hit:hitUPath];
	} else if(sibling)
		return [sibling hit:hitUPath];
	return nil;	
}

- hitAttachment:(const NXPoint *)aPoint :(BOOL *)theAttach
{
	BOOL didHit;
	id dummy;
	NXRect dummyRect;

	*theAttach = NO;
	if(hasVerknuepfung){
		NXSetRect(&dummyRect, 
			pos.x + 23, 
			pos.y - linewidth - attachCellFrame.size.height - 1, 
			attachCellFrame.size.width, 
			attachCellFrame.size.height);
		didHit = NXPointInRect(aPoint, &dummyRect);
	} else
		didHit = NO;
	if(didHit){
		*theAttach = YES;
		return self;
	}
	if(hasSound){
		NXSetRect(&dummyRect, 
			pos.x + 37, 
			pos.y - linewidth - soundCellFrame.size.height - 1, 
			soundCellFrame.size.width, 
			soundCellFrame.size.height);
		didHit = NXPointInRect(aPoint, &dummyRect);
	} else
		didHit = NO;
	if(didHit)
		return self;
	if(child){
		dummy = [child hitAttachment:aPoint :theAttach];
		if(dummy)
			return dummy;
		else if(sibling)
			return [sibling hitAttachment:aPoint :theAttach];
	} else if(sibling)
		return [sibling hitAttachment:aPoint :theAttach];
	return nil;	
}

- activateAttachment:(NXEvent *)event inView:aView
{
	NXPoint p;
	NXRect dummyRect;
	id result;
	
	if(!hasVerknuepfung)
		return self;
	attachCellFrame.origin.x = pos.x + 23;
	attachCellFrame.origin.y = pos.y - linewidth - attachCellFrame.size.height - 1;
	[attachCell highlight:&attachCellFrame inView:aView lit:YES];
	[[aView window] flushWindow];
	while(event->type != NX_MOUSEUP)
		event = [NXApp getNextEvent:NX_MOUSEUPMASK];
	p = event->location; 
    [aView convertPoint:&p fromView:nil];
	dummyRect = attachCellFrame;
	if(NXPointInRect(&p, &dummyRect)){
		[[Application workspace] openFile:[verknuepfung sourceFilename]];
		result = self;
	} else
		result = nil;
	[attachCell highlight:&attachCellFrame inView:aView lit:NO];
	[[aView window] flushWindow];
	return result;
}

- activateSound:(NXEvent *)event inView:aView
{
	NXPoint p;
	NXRect dummyRect;
	id result;
	
	if(!hasSound)
		return self;
	soundCellFrame.origin.x = pos.x + 37;
		soundCellFrame.origin.y = pos.y - linewidth - soundCellFrame.size.height - 1;
	[soundCell highlight:&soundCellFrame inView:aView lit:YES];
	[[aView window] flushWindow];
	while(event->type != NX_MOUSEUP)
		event = [NXApp getNextEvent:NX_MOUSEUPMASK];
	p = event->location; 
    [aView convertPoint:&p fromView:nil];
	dummyRect = soundCellFrame;
	if(NXPointInRect(&p, &dummyRect)){
		[geraeusch play];
		result = self;
	} else
		result = nil;
	[soundCell highlight:&soundCellFrame inView:aView lit:NO];
	[[aView window] flushWindow];
	return result;
}

- drawNodeShadow:(DrawState *)aState
{
	NXPoint dummy;

	if(shadow){
		if(!NXEqualColor(aState->color,shadowColor)){
			aState->color = shadowColor;
			NXSetColor(shadowColor);
		}	
		dummy = pos;
		pos.x += deltaShadow.x;
		pos.y += deltaShadow.y;
		[self changePos];
		[gpath send:dps_ufill cached:YES];
		pos = dummy;
		[self changePos];
	}
	return self;
}

- drawNodeFill:(DrawState *)aState
{
	if(fill){
		if(!NXEqualColor(aState->color,fillColor)){
			aState->color = fillColor;
			NXSetColor(fillColor);
		}
		[gpath send:dps_ufill cached:YES];
	}
	return self;
}

- drawNodeEnding:(DrawState *)aState
{
	NXPoint outPoint,iPoint;
	float rotAngle;
	Tree *ptr;
	
	if(ending != ENDING_NONE && parent){
		if(linkKind)
			[self linkChildPoint:&outPoint];
		else 
			[parent linkPoint:&outPoint];
		if(!NXEqualColor(aState->color,NX_COLORBLACK)){
			aState->color = NX_COLORBLACK;
			NXSetColor(NX_COLORBLACK);
		}
		if(aState->linewidth != 0.15){
			aState->linewidth = 0.15;
			PSsetlinewidth(0.15);
		}
		[self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint];
		switch(ending){
			case ENDING_ARROW:
				PSWEndingSolid(arrowEnding->bboxParams, arrowEnding->sizeParams + 4,
						arrowEnding->bboxOps, arrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
			break;
			case ENDING_HOLLOW:
				PSWEndingHollow(arrowEnding->bboxParams, arrowEnding->sizeParams + 4,
						arrowEnding->bboxOps, arrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
			break;
			case ENDING_DOUBLE:
				PSWEndingSolid(doubleEnding->bboxParams, doubleEnding->sizeParams + 4,
						doubleEnding->bboxOps, doubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
			break;
			case ENDING_SOLID:
				PSWEndingSolid(circleEnding->bboxParams, circleEnding->sizeParams + 4,
						circleEnding->bboxOps, circleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
			break;
			case ENDING_CIRCLE:
				PSWEndingHollow(circleEnding->bboxParams, circleEnding->sizeParams + 4,
						circleEnding->bboxOps, circleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
			break;
			default:
			break;
		}
	}
	if(parentEnding != ENDING_NONE && child){
		if(linkKind){
			[self linkParentPoint:&outPoint];
			[self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint];
			switch(parentEnding){
				case ENDING_ARROW:
					PSWEndingSolid(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
						parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
				break;
				case ENDING_HOLLOW:
					PSWEndingHollow(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
						parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
				break;
				case ENDING_DOUBLE:
					PSWEndingSolid(parentDoubleEnding->bboxParams, parentDoubleEnding->sizeParams + 4,
						parentDoubleEnding->bboxOps, parentDoubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
				break;
				case ENDING_SOLID:
					PSWEndingSolid(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
						parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
				break;
				case ENDING_CIRCLE:
					PSWEndingHollow(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
						parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
				break;
				default:
				break;
			}
		} else {
			for(ptr = child;ptr;ptr = ptr->sibling){
				[ptr linkPoint:&outPoint];
				[self calcIntersection:&iPoint angle:&rotAngle toPoint:&outPoint];
				switch(parentEnding){
					case ENDING_ARROW:
						PSWEndingSolid(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
							parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
					break;
					case ENDING_HOLLOW:
						PSWEndingHollow(parentArrowEnding->bboxParams, parentArrowEnding->sizeParams + 4,
							parentArrowEnding->bboxOps, parentArrowEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
					break;
					case ENDING_DOUBLE:
						PSWEndingSolid(parentDoubleEnding->bboxParams, parentDoubleEnding->sizeParams + 4,
							parentDoubleEnding->bboxOps, parentDoubleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
					break;
					case ENDING_SOLID:
						PSWEndingSolid(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
							parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
					break;
					case ENDING_CIRCLE:
						PSWEndingHollow(parentCircleEnding->bboxParams, parentCircleEnding->sizeParams + 4,
							parentCircleEnding->bboxOps, parentCircleEnding->sizeOps + 2,iPoint.x,iPoint.y,rotAngle);
					break;
					default:
					break;
				}	
			}
		}
	}
	return self;
}

- drawNodeOutline:(DrawState *)aState knobs:(BOOL)aBool
{
	if(outline){
		if(!NXEqualColor(aState->color,outlineColor)){
			aState->color = outlineColor;
			NXSetColor(outlineColor);
		}
		if(aState->linewidth != linewidth){
			aState->linewidth = linewidth;
			PSsetlinewidth(linewidth);
		}
		[gpath send:dps_ustroke cached:YES];
	}
	if(selected && aBool){
		if(delegate)
			PSselectfont(fontname,officialFontSize / [delegate docScale]);
		else 
			PSselectfont(fontname,officialFontSize);
		aState->font = nil;
		if(!NXEqualColor(aState->color,NX_COLORBLACK)){
			aState->color = NX_COLORBLACK;
			NXSetColor(NX_COLORBLACK);
		}
		controlX = gpath->bbox[0];
		controlY = gpath->bbox[1];
		controlPts[0] = gpath->bbox[2] - gpath->bbox[0];
		controlPts[1] = 0;
		controlPts[2] = 0; 
		controlPts[3] = gpath->bbox[3] - gpath->bbox[1];
		controlPts[4] = -controlPts[0];
		controlPts[5] = 0;
		PSWDrawControlPoints(controlX,controlY,controlPts,8,controlChars);
		if(!NXEqualColor(aState->color,NX_COLORDKGRAY)){
			aState->color = NX_COLORDKGRAY;
			NXSetColor(NX_COLORDKGRAY);
		}
		PSWDrawControlPoints(controlX,controlY,controlPts,8,controlPrimChars);
	}
	return self;
}

- drawNodeCellsInView:aView attachments:(BOOL)aBool
{	
	if(hasVerknuepfung && aBool){
		attachCellFrame.origin.x = pos.x + 23;
		attachCellFrame.origin.y = pos.y - linewidth - attachCellFrame.size.height - 1;
		[attachCell drawSelf:&attachCellFrame inView:aView];
	}
	if(hasSound && aBool){
		soundCellFrame.origin.x = pos.x + 37;
		soundCellFrame.origin.y = pos.y - linewidth - soundCellFrame.size.height - 1;
		[soundCell drawSelf:&soundCellFrame inView:aView];
	}
	textCellFrame.origin.x = pos.x + deltaText.x;
	textCellFrame.origin.y = pos.y + deltaText.y;
	[textCell drawSelf:&textCellFrame inView:aView];
	if(aBool && zipped){
		zippedCellFrame.origin.x = pos.x + 7;
		zippedCellFrame.origin.y = pos.y - linewidth - zippedCellFrame.size.height - 1;
		[zippedCell drawSelf:&zippedCellFrame inView:aView];
	}
	return self;	
}

- setOffset:(NXCoord)aX :(NXCoord)aY
{
   	offset.x = aX;
   	offset.y = aY;
   	return self;
}

- (NXCoord)width
{
   	return width;
}

- (NXCoord)height
{
   	return height;
}

- (Polygon *)contour
{
   	return &contour;
}

- layout
{
    Tree *c;
 	
	if(!updateLayout)
		return nil;
	if(zipped){
		[self layoutLeaf];
		updateLayout = NO;
		return self;
	}
    for(c = child;c;c = [c sibling])
          [c layout];
    if(child)
          [self attachParent:[self join]];
    else
          [self layoutLeaf];
	updateLayout = NO;
	return self;
}

- getTextFrame:(NXRect *)aRect
{
	NXSetRect(aRect, pos.x, pos.y + deltaText.y, width, textCellFrame.size.height);
	return self;
}

//************************************************************************
// tree data structure manipulation

- sibling
{
   	return sibling;
}

- child
{
	return child;
}

- parent
{
	return parent;
}

- addTree:(Tree *)aTree
{
	Tree *dummy;
	id undoManager;
	
	aTree->parent = self;
	aTree->linkKind = linkKind;
	aTree->biegFactor = biegFactor;
	aTree->parentDistance = parentDistance;
	aTree->shadow = shadow;
	aTree->shadowColor = shadowColor;
	aTree->border = border;
	if(!child){
		child = aTree;
	} else {
		dummy = child;
		while(dummy->sibling)
			dummy = dummy->sibling;
		dummy->sibling = aTree;
		aTree->sibling = nil;
	}
	[self setUpdateLayout:YES];
	if(delegate){
		undoManager = [delegate undoManager];
		[undoManager setUndoName:"Addition"];
		[undoManager setRedoName:"Deletion"];
		[[undoManager setUndoTarget:self] removeChild:aTree];
		[delegate declareSelection:aTree];
		[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_ALL | SCROLL_TREEVIEW)];
	}
	return self;
}

- removeChild:(Tree *)aChild
{
	Tree *dummy;
	id undoManager;
	int i;
	
	if(!child || !aChild)
		return self;
	[self setUpdateLayout:YES];
	dummy = child;
	i = 0;
	if(dummy == aChild)
		child = aChild->sibling;
	else {
		while(dummy->sibling && dummy->sibling != aChild){
			dummy = dummy->sibling;
			i++;
		}
		if(dummy->sibling == aChild)
			dummy->sibling = aChild->sibling;
	}
	aChild->parent = nil;
	aChild->sibling = nil;
	if(delegate){
		[delegate declareSelection:nil];
		undoManager = [delegate undoManager];
		[undoManager freeUndoArgsOnRecordDiscard];
		[undoManager setUndoName:"Deletion"];
		[undoManager setRedoName:"Addition"];
		[[undoManager setUndoTarget:self] insertTree:aChild at:i];
		[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_ALL];
	} else
		[aChild free];
	return self;
}

- insertTree:(Tree *)aTree at:(int)aPos
{
	Tree *dummy;
	id undoManager;
	int i;
	
	aTree->parent = self;
	aTree->linkKind = linkKind;
	aTree->biegFactor = biegFactor;
	aTree->parentDistance = parentDistance;
	aTree->shadow = shadow;
	aTree->shadowColor = shadowColor;
	aTree->border = border;
	if(!child){
		child = aTree;
	} else {
		dummy = child;
		i = 0;
		while(dummy->sibling && i < aPos)
			dummy = dummy->sibling;
		aTree->sibling = dummy->sibling;
		dummy->sibling = aTree;
	}
	[self setUpdateLayout:YES];
	if(delegate){
		undoManager = [delegate undoManager];
		[undoManager setUndoName:"Addition"];
		[undoManager setRedoName:"Deletion"];
		[[undoManager setUndoTarget:self] removeChild:aTree];
		[delegate declareSelection:aTree];
		[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_ALL | SCROLL_TREEVIEW)];
	}
	return self;
}

	
//************************************************************************
// properties

- attachFile:(const char *)aFileName
{
	NXRect r;
	BOOL attachUpdate = YES;
	
	if(verknuepfung){
		[verknuepfung free];
		attachUpdate = NO;
	}
	verknuepfung = [[NXDataLink allocFromZone:[self zone]] initLinkedToFile:aFileName];
	if(attachUpdate){
		hasVerknuepfung = YES;
		[self getBounds:&r];
		[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	}
	return self;
}

- breakAttachment
{
	NXRect r;
	
	if(!hasVerknuepfung)
		return self;
	[self getBounds:&r];
	hasVerknuepfung = NO; 
	[verknuepfung free];
	verknuepfung = nil;
	[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	return self;
}

- (BOOL)attached
{
	return hasVerknuepfung;
}

- attachSound:(const char *)aSoundName
{
	NXRect r;
	BOOL soundUpdate = YES;
	
	if(geraeusch){
		[geraeusch free];
		soundUpdate = NO;
	}
	geraeusch = [[Sound allocFromZone:[self zone]] initFromSoundfile:aSoundName];
	if(soundUpdate){
		hasSound = YES;
		[self getBounds:&r];
		[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	}
	return self;
}

- breakSound
{
	NXRect r;
	
	if(!hasSound)
		return self;
	[self getBounds:&r];
	hasSound = NO; 
	[geraeusch free];
	geraeusch = nil;
	[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	return self;
}

- (BOOL)hasSound
{
	return hasSound;
}

- attachedSound
{
	return geraeusch;
}

- setSound:aSound
{
	NXRect r;
	BOOL soundUpdate = YES;
	
	if(geraeusch){
		[geraeusch free];
		soundUpdate = NO;
	}
	geraeusch = aSound;
	if(soundUpdate){
		hasSound = YES;
		[self getBounds:&r];
		[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	}
	return self;
}

- applyStyleToSubtree
{
	Properties props;
	
	if(child){
		[self getProps:&props];
		[child promoteProps:&props];
		[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	}
	return self;
}

- promoteProps:(Properties *)aProps
{
	outline = aProps->outline;
	if([textCell font] != aProps->font){
		[textCell setFont:aProps->font];
		[self changeText];
		[self setUpdateLayout:YES];
	}
	if(pathKind != aProps->pathKind){
		[gpath resetFill];
		pathKind = aProps->pathKind;
		[self makePath];
		[self setUpdateLayout:YES];
	}
	fillColor = aProps->fillColor;
	outlineColor = aProps->outlineColor;
	[textCell setTextColor:aProps->textColor];
	linewidth = aProps->linewidth;
	if(ending != aProps->ending){
		ending = aProps->ending;
	}
	if(parentEnding != aProps->parentEnding){
		parentEnding = aProps->parentEnding;
	}
	if(sibling)
		[sibling promoteProps:aProps];
	if(child)
		[child promoteProps:aProps];
	return self;
}

- getProps:(Properties *)aProps
{
	aProps->outline = outline;
	aProps->font = [textCell font];
	aProps->pathKind = pathKind;
	aProps->fillColor = fillColor;
	aProps->outlineColor = outlineColor;
	aProps->textColor = [textCell textColor];
	aProps->linewidth = linewidth;
	aProps->ending = ending;
	aProps->parentEnding = parentEnding;
	return self;
}

- changeTo:(Properties *)aProps
{
	NXRect r, dummyRect, pr;
	BOOL checkResize, makeUnion;
	
	[self getBounds:&r];
	checkResize = NO;
	makeUnion = NO;
	outline = aProps->outline;
	if([textCell font] != aProps->font){
		[textCell setFont:aProps->font];
		[self changeText];
		[self setUpdateLayout:YES];
		checkResize = YES;
	}
	if(pathKind != aProps->pathKind){
		[gpath resetFill];
		pathKind = aProps->pathKind;
		[self makePath];
		[self setUpdateLayout:YES];
		checkResize = YES;
	}
	fillColor = aProps->fillColor;
	outlineColor = aProps->outlineColor;
	[textCell setTextColor:aProps->textColor];
	if(ending != aProps->ending){
		ending = aProps->ending;
		makeUnion = YES;
	}
	if(parentEnding != aProps->parentEnding){
		parentEnding = aProps->parentEnding;
		if(parent){
			[parent getBounds:&pr];
			[delegate updateViewsDirty:YES rect:&pr flag:UPDATE_TREEVIEW];
		}
	}
	if(aProps->linewidth != linewidth){
		linewidth = aProps->linewidth;
		makeUnion = YES;
	}
	if(makeUnion){
		[self getBounds:&dummyRect];
		NXUnionRect(&dummyRect, &r);
	}
	if(checkResize)
		[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	else
		[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	return self;
}
	
//************************************************************************
// tree properties

- (NXCoord)border
{
   	return border;
}

- setBorder:(NXCoord)aBorder
{
	border = aBorder;
	[self setUpdateLayout:YES];
	if(child)
		[child setBorder:aBorder];
	if(sibling)
		[sibling setBorder:aBorder];
	if(!parent)
		[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	return self;
}

- (NXCoord)parentDistance
{
	return parentDistance;
}

- setParentDistance:(NXCoord)aParentDistance
{
	parentDistance = aParentDistance;
	[self setUpdateLayout:YES];
	if(child)
		[child setParentDistance:aParentDistance];
	if(sibling)
		[sibling setParentDistance:aParentDistance];
	if(!parent)
		[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	return self;
}

- (BOOL)shadow
{
	return shadow;
}

- setShadow:(BOOL)aShadow
{	
	shadow = aShadow;
	if(child)
		[child setShadow:aShadow];
	if(sibling)
		[sibling setShadow:aShadow];
	if(!parent)
		[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	return self;
}

- (NXColor)shadowColor
{
	return shadowColor;
}

- setShadowColor:(NXColor)aColor
{	
	shadowColor = aColor;
	if(child)
		[child setShadowColor:aColor];
	if(sibling)
		[sibling setShadowColor:aColor];
	if(!parent)
		[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
	return self;
}

- (int)linkKind
{
	return linkKind;
}

- setLinkKind:(int)aLinkKind
{
	linkKind = aLinkKind;
	if(child)
		[child setLinkKind:aLinkKind];
	if(sibling)
		[sibling setLinkKind:aLinkKind];
	if(!parent)
		[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
	return self;
}

- changeLinkKind
{
	linkKind = (linkKind + 1) % 2;
	if(child)
		[child changeLinkKind];
	if(sibling)
		[sibling changeLinkKind];
	if(!parent)
		[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
	return self;
}

- (float)biegFactor
{
	return biegFactor;
}

- setBiegFactor:(float)aBiegFactor
{
	biegFactor = aBiegFactor;
	if(child)
		[child setBiegFactor:aBiegFactor];
	if(sibling)
		[sibling setBiegFactor:aBiegFactor];
	if(!parent)
		[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TREEVIEW];
	return self;
}

//************************************************************************
// node properties

- (const char *)label
{
	return [textCell stringValue];
}

- setLabel:(const char *)aLabel
{	
	[textCell setStringValue:aLabel];
	[self changeText];
	[self setUpdateLayout:YES];
	[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	return self;
}

- setRTFDescription:(const char *)aDescription
{
	[nodeDescription setRTF:aDescription];
	[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TEXTVIEW];
	return self;
}

- setDescription:(const char *)aDescription
{	
	[nodeDescription setAscii:aDescription];
	[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_TEXTVIEW];
	return self;
}

- changeDescriptionFrom:aTextView
{
	[nodeDescription setRTFFromTextView:aTextView];
	[delegate updateViewsDirty:YES rect:NULL flag:UPDATE_NONE];
	return self;	
}

- putDescriptionIn:aTextView
{
	[aTextView replaceSelWithRichText:[nodeDescription stream]];
	[nodeDescription freeStream];
	return self;
}

- (int)pathKind
{
	return pathKind;
}

- setPathKind:(int)aPathKind
{
	[gpath resetFill];
	pathKind = aPathKind;
	[self makePath];
	[self setUpdateLayout:YES];
	[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	return self;
}

- (BOOL)outline
{
	return outline;
}

- setOutline:(BOOL)aOutline
{
	NXRect r;
	
	outline = aOutline;
	[self getBounds:&r];
	[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	return self;
}

- font
{
	return [textCell font];
}

- changeFont:sender
{
	[textCell setFont:[sender convertFont:[textCell font]]];
	[self changeText];
	[self setUpdateLayout:YES];
	[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	return self;
}

- (NXColor)fillColor
{
	return fillColor;
}

- setFillColor:(NXColor)aColor
{
	NXRect r;
	
	fillColor = aColor;
	[self getBounds:&r];
	[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	return self;
}

- (NXColor)outlineColor
{
	return outlineColor;
}

- setOutlineColor:(NXColor)aColor
{
	NXRect r;
	
	outlineColor = aColor;
	[self getBounds:&r];
	[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	return self;
}

- (NXColor)textColor
{
	return [textCell textColor];
}

- setTextColor:(NXColor)aColor
{
	NXRect r;
	
	[textCell setTextColor:aColor];
	[self getBounds:&r];
	[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	return self;
}

- (float)linewidth
{
	return linewidth;
}

- setLinewidth:(float)aLinewidth
{
	NXRect r;
	
	if(aLinewidth > linewidth){
		linewidth = aLinewidth;
		[self getBounds:&r];
	} else {
		[self getBounds:&r];
		linewidth = aLinewidth;
	}
	[delegate updateViewsDirty:YES rect:&r flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	return self;
}

- (BOOL)zipped
{
	return zipped;
}

- changeZipped
{
	zipped = ! zipped;
	[self setUpdateLayout:YES];
	[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	return self;
}

- (int)ending
{
	return ending;
}

- setEnding:(int)aEnding
{
	NXRect r,br;
	
	[self getBounds:&br];
	ending = aEnding;
	[self getBounds:&r];
	NXUnionRect(&br,&r);
	[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	return self;
}

- (int)parentEnding;
{
	return parentEnding;
}

- setParentEnding:(int)aEnding
{
	NXRect r,br;
	
	[self getBounds:&br];
	parentEnding = aEnding;
	[self getBounds:&r];
	NXUnionRect(&br,&r);
	[delegate updateViewsDirty:YES rect:&r flag:UPDATE_TREEVIEW];
	return self;
}
		
//************************************************************************
// misc

- (BOOL)selected
{
	return selected;
}

- setSelected:(BOOL)aBOOL
{
	selected = aBOOL;
	return self;
}
	 
- setUpdateLayout:(BOOL)aBool
{
	Tree *dummy;

	updateLayout = aBool;
	if(aBool)
		if(parent){
			[parent setUpdateLayout:YES];
			dummy = [parent child];
			dummy->updateLayout = YES;
			while([dummy sibling]){
				dummy = [dummy sibling];
				dummy->updateLayout = YES;	
			}
		}
	return self;
}

- positionFrom:(const NXPoint *)aPoint 
{
	pos.x = aPoint->x + offset.x;
	pos.y = aPoint->y + offset.y;
	[self changePos];
	if(child)
		[child positionFrom:&pos];
	if(sibling)
		[sibling positionFrom:&pos];
	return self;
}

- setPositionTo:(const NXPoint *)aPoint
{
	pos.x = aPoint->x;
	pos.y = aPoint->y;
	[self changePos];
	if(child)
		[child positionFrom:&pos];
	if(sibling)
		[sibling positionFrom:&pos];
	return self;
}

- linkPoint:(NXPoint *)aPoint
{
	aPoint->x = pos.x + deltaLink.x;
	aPoint->y = pos.y + deltaLink.y;
	return self;
}

- linkParentPoint:(NXPoint *)aPoint
{
	aPoint->x = pos.x + width + (parentDistance + border) * (1 - biegFactor) / 2;
	aPoint->y = pos.y + deltaLink.y;
	return self;
}

- linkChildPoint:(NXPoint *)aPoint
{
	aPoint->x = pos.x - (parentDistance + border) * (1 - biegFactor) / 2;
	aPoint->y = pos.y + deltaLink.y;
	return self;
}

- delegate
{
	return delegate;
}

- setDelegate:aDelegate
{
	id oldDelegate;
	
	oldDelegate = delegate;
	delegate = aDelegate;
	return oldDelegate;
}

- setDelegateRecursive:aDelegate
{
	delegate = aDelegate;
	if(child)
		[child setDelegateRecursive:aDelegate];
	if(sibling)
		[sibling setDelegateRecursive:aDelegate];
	return self;
}

- textDidEnd:textObject endChar:(unsigned short)endChar
{
	int maxlen,length;
    char *buffer;
    NXStream *stream;
	
	[textObject removeFromSuperview];
	[textObject setDelegate:nil];
	[textObject setSel:0 :0];
	if([textObject textLength]){
		stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
	    [textObject writeText:stream];
	    NXGetMemoryBuffer(stream, &buffer, &length, &maxlen);
	    [textCell setStringValue:buffer];
	    NXCloseMemory(stream, NX_FREEBUFFER);
	}
	[self changeText];
	[self setUpdateLayout:YES];
	[delegate updateViewsDirty:YES rect:NULL flag:(UPDATE_TREEVIEW | CHECK_RESIZE)];
	return self;
}

//*************************************************************************
// streaming to diagram2 format


- (unsigned)dgSymbolNr
{
	return dgSymbolNr;
}

- dgWriteSymbol:(NXStream *)stream buffer:(char *)buffer filename:(const char *)aName
{
	id font;
	char rtfItalic[3], rtfBold[3];
	unsigned i;
	int err;

	dgSymbolNr = [Tree dgNextNr];
	sprintf(buffer,"symbol %d\n", dgSymbolNr);
	NXWrite(stream,buffer,strlen(buffer));
	sprintf(buffer,"\tshape \"%s\"\n", dgShapeSymbolName[pathKind]);
	NXWrite(stream,buffer,strlen(buffer));
	sprintf(buffer,"\tlocation %.2f %.2f\n",pos.x,pos.y);
	NXWrite(stream,buffer,strlen(buffer));
	sprintf(buffer,"\tsize %.2f %.2f\n",width,height);
	NXWrite(stream,buffer,strlen(buffer));
	if(outline)
		NXWrite(stream,"\tframed\n", 8);
	if(fill)
		NXWrite(stream,"\tfilled\n", 8);
	if(shadow)
		NXWrite(stream,"\tshadowed\n", 10);
	if(outline){
		i = 0;
		while(i < [dgColorList colorCount] && 
			!NXEqualColor(outlineColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]]))
			i++;
		if(i == [dgColorList colorCount]){
			sprintf(buffer,"woodColorIndexed%d",i);
			[dgColorList setColorNamed:buffer color:outlineColor];
		}
		sprintf(buffer,"\tframeColor colorIndex %d\n",i);
		NXWrite(stream,buffer,strlen(buffer));
	}
	if(fill){
		i = 0;
		while(i < [dgColorList colorCount] && 
			!NXEqualColor(fillColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]]))
			i++;
		if(i == [dgColorList colorCount]){
			sprintf(buffer,"woodColorIndexed%d",i);
			[dgColorList setColorNamed:buffer color:fillColor];
		}
		sprintf(buffer,"\tfillColor colorIndex %d\n",i);
		NXWrite(stream,buffer,strlen(buffer));
	}
	if(shadow){
		i = 0;
		while(i < [dgColorList colorCount] && 
			!NXEqualColor(shadowColor,[dgColorList colorNamed:[dgColorList nameOfColorAt:i]]))
			i++;
		if(i == [dgColorList colorCount]){
			sprintf(buffer,"woodColorIndexed%d",i);
			[dgColorList setColorNamed:buffer color:shadowColor];
		}
		sprintf(buffer,"\tshadowColor colorIndex %d\n",i);
		NXWrite(stream,buffer,strlen(buffer));
	}
	sprintf(buffer,"\tlineWidth %.2f\n",linewidth);
	NXWrite(stream,buffer,strlen(buffer));
	if(hasVerknuepfung){
		sprintf(buffer,"\tattachment \"%s\"\n",[verknuepfung sourceFilename]);
		NXWrite(stream,buffer,strlen(buffer));
	}	
	if(hasSound){
		sprintf(buffer,"%s/Sound%d.snd",aName,dgSymbolNr);
		err = [geraeusch writeSoundfile:buffer];
		if(err)
			NXRunAlertPanel("Save Sound","Cannot write %s","OK",NULL,NULL,buffer);
		else {
			sprintf(buffer,"\tsoundFile \"Sound%d.snd\"\n",dgSymbolNr);
			NXWrite(stream,buffer,strlen(buffer));
		}
	}
	font = [textCell font];
	if(strstr([font displayName], "Bold"))
		strcpy(rtfBold,"b");
	else
		strcpy(rtfBold,"b0");
	if(strstr([font displayName], "Italic") || strstr([font displayName], "Oblique"))
		strcpy(rtfItalic,"i");
	else
		strcpy(rtfItalic,"i0");
	sprintf(buffer, dgRtfControlName, [font familyName], rtfBold, rtfItalic, [font pointSize] * 2, [textCell stringValue]);
	NXWrite(stream,buffer,strlen(buffer));
	NXWrite(stream,"\n",1);
	NXWrite(stream,"\ttextPlacement middle\n", 22);
	NXWrite(stream,dgEndName, strlen(dgEndName));
	if(child)
		[child dgWriteSymbol:stream buffer:buffer filename:aName];
	if(sibling)
		[sibling dgWriteSymbol:stream buffer:buffer filename:aName];
	return self;
}

- dgWriteVertex:(NXStream *)stream buffer:(char *)buffer
{
	NXPoint p2,p3;

	if(parent){
		dgVertexNr[0] = [Tree dgNextNr];
		sprintf(buffer,"vertex %d\n",dgVertexNr[0]);
		NXWrite(stream,buffer,strlen(buffer)); 
		NXWrite(stream,dgEndName, strlen(dgEndName));
		dgVertexNr[1] = [Tree dgNextNr];
		sprintf(buffer,"vertex %d\n",dgVertexNr[1]);
		NXWrite(stream,buffer,strlen(buffer)); 
		NXWrite(stream,dgEndName, strlen(dgEndName));
		if(linkKind){
			[parent linkParentPoint:&p3];
			[self linkChildPoint:&p2];
			dgVertexNr[2] = [Tree dgNextNr];
			sprintf(buffer,"vertex %d\n",dgVertexNr[2]);
			NXWrite(stream,buffer,strlen(buffer)); 
			sprintf(buffer,"\tlocation %.2f %.2f\n", p2.x, p2.y);
			NXWrite(stream,buffer,strlen(buffer));
			NXWrite(stream,dgEndName, strlen(dgEndName));
			dgVertexNr[3] = [Tree dgNextNr];
			sprintf(buffer,"vertex %d\n",dgVertexNr[3]);
			NXWrite(stream,buffer,strlen(buffer)); 
			sprintf(buffer,"\tlocation %.2f %.2f\n", p3.x, p3.y);
			NXWrite(stream,buffer,strlen(buffer));
			NXWrite(stream,dgEndName, strlen(dgEndName));
		}
	}
	if(child)
		[child dgWriteVertex:stream buffer:buffer];
	if(sibling)
		[sibling dgWriteVertex:stream buffer:buffer];
	return self;
}

- dgWriteLine:(NXStream *)stream buffer:(char *)buffer
{
	if(parent){
		sprintf(buffer,"line %d\n",[Tree dgNextNr]);
		NXWrite(stream,buffer,strlen(buffer));
		sprintf(buffer,"\tfrom %d\n",dgSymbolNr);
		NXWrite(stream,buffer,strlen(buffer));
		sprintf(buffer,"\tto %d\n",[parent dgSymbolNr]);
		NXWrite(stream,buffer,strlen(buffer));
		if(ending != ENDING_NONE){
			switch(ending){
				case ENDING_ARROW:
					sprintf(buffer,"\ttailType arrow\n");
				break;
				case ENDING_HOLLOW:
					sprintf(buffer,"\ttailType hollowArrow\n");
				break;
				case ENDING_DOUBLE:
					sprintf(buffer,"\ttailType doubleArrow\n");
				break;
				case ENDING_SOLID:
					sprintf(buffer,"\ttailType solidCircle\n");
				break;
				case ENDING_CIRCLE:
					sprintf(buffer,"\ttailType circle\n");
				break;
				default:
				break;
			}
			NXWrite(stream,buffer,strlen(buffer));
		}
		if([parent parentEnding] != ENDING_NONE){
			switch([parent parentEnding]){
				case ENDING_ARROW:
					sprintf(buffer,"\theadType arrow\n");
				break;
				case ENDING_HOLLOW:
					sprintf(buffer,"\theadType hollowArrow\n");
				break;
				case ENDING_DOUBLE:
					sprintf(buffer,"\theadType doubleArrow\n");
				break;
				case ENDING_SOLID:
					sprintf(buffer,"\theadType solidCircle\n");
				break;
				case ENDING_CIRCLE:
					sprintf(buffer,"\theadType circle\n");
				break;
				default:
				break;
			}
			NXWrite(stream,buffer,strlen(buffer));
		}
		sprintf(buffer,"\t%d\n",dgVertexNr[0]);
		NXWrite(stream,buffer,strlen(buffer));
		if(linkKind){
			sprintf(buffer,"\t%d\n",dgVertexNr[2]);
			NXWrite(stream,buffer,strlen(buffer));
			sprintf(buffer,"\t%d\n",dgVertexNr[3]);
			NXWrite(stream,buffer,strlen(buffer));
		}	
		sprintf(buffer,"\t%d\n",dgVertexNr[1]);
		NXWrite(stream,buffer,strlen(buffer));
		NXWrite(stream,dgEndName, strlen(dgEndName));
	}
	if(child)
		[child dgWriteLine:stream buffer:buffer];
	if(sibling)
		[sibling dgWriteLine:stream buffer:buffer];
	return self;
}

+ saveToD2Tree:aTree fromView:aView toFile:(const char *)aFile printInfo:aPrintInfo
{
	NXStream *stream;
	NXTypedStream *typedStream;
	char fileNameBuffer[MAXPATHLEN],commandBuffer[MAXPATHLEN + 20];
	char buffer[1024];
	NXRect dummyRect; 
	
	dgColorList = [[NXColorList alloc] init];
	[aView getBounds:&dummyRect];
	dgNextNr = 0;
	stream = NXOpenMemory(NULL,0,NX_WRITEONLY);
	NXWrite(stream, dgDocuBegin,strlen(dgDocuBegin));
	strcpy(fileNameBuffer,aFile);
	sprintf(commandBuffer,"mkdir %s",fileNameBuffer);
	system(commandBuffer);
	if(aTree){
		NXWrite(stream, dgSymbolComment,strlen(dgSymbolComment));
		[aTree dgWriteSymbol:stream buffer:buffer filename:aFile];
		NXWrite(stream, dgVertexComment,strlen(dgVertexComment));
		[aTree dgWriteVertex:stream buffer:buffer];
		NXWrite(stream, dgLineComment,strlen(dgLineComment));
		[aTree dgWriteLine:stream buffer:buffer];
	}
	strcpy(fileNameBuffer,aFile);
	strcat(fileNameBuffer,"/DiagramText");
	NXSaveToFile(stream, fileNameBuffer); 	   	  
	NXCloseMemory(stream,NX_FREEBUFFER);
	strcpy(fileNameBuffer,aFile);
	strcat(fileNameBuffer,"/PrintInfo");
	stream = NXOpenMemory(NULL,0,NX_WRITEONLY);
	typedStream = NXOpenTypedStream(stream,NX_WRITEONLY);
	NXWriteRootObject(typedStream,aPrintInfo);
	NXCloseTypedStream(typedStream);
	NXSeek(stream, - 1, NX_FROMCURRENT);
	NXWrite(stream,dgPrintInfoEnd,6);
	NXSaveToFile(stream, fileNameBuffer);
	NXCloseMemory(stream,NX_FREEBUFFER);
	strcpy(fileNameBuffer,aFile);
	strcat(fileNameBuffer,"/Colors.clr");
	[dgColorList saveTo:fileNameBuffer];
	[dgColorList free];
	dgColorList = nil;
	return self;
}

+ (unsigned)dgNextNr
{
	dgNextNr++;
	return dgNextNr;
}

//*************************************************************************
// streaming

- writeStyleToPBStream:(NXTypedStream *)stream
{
	NXWriteTypes(stream,"iiiif",&outline,&pathKind,&ending,&parentEnding,&linewidth);
	NXWriteColor(stream,fillColor);
	NXWriteColor(stream,outlineColor);
	NXWriteColor(stream,[textCell textColor]);
	NXWriteObject(stream,[textCell font]);
	return self;
}

- readStyleFromPBStream:(NXTypedStream *)stream
{
	Properties props;
	
	NXReadTypes(stream,"iiiif",&(props.outline),&(props.pathKind),&(props.ending),&(props.parentEnding),&(props.linewidth));
	props.fillColor = NXReadColor(stream);
	props.outlineColor = NXReadColor(stream);
	props.textColor = NXReadColor(stream);
	props.font = NXReadObject(stream);
	[self changeTo:&props];
	return self;
}

- writeToPBStream:(NXTypedStream *)stream
{
	Tree *tmpParent, *tmpSibling;
	
	tmpParent = parent;
	tmpSibling = sibling;
	parent = sibling = nil;
	NXWriteRootObject(stream, self);
	parent = tmpParent;
	sibling = tmpSibling;
	return self;
}

- writeToMMAPB:(NXStream *)stream
{
	if(child){
		NXWrite(stream,"{",1);
		NXWrite(stream,[textCell stringValue],strlen([textCell stringValue]));
		NXWrite(stream,", ",2);
		[child writeToMMAPB:stream];
		NXWrite(stream,"}",1);
	} else {
		NXWrite(stream,[textCell stringValue],strlen([textCell stringValue]));
	}
	if(sibling){
		NXWrite(stream,", ",2);
		[sibling writeToMMAPB:stream];
	}
	return self;
}

- write:(NXTypedStream *)stream
{
	//BOOL hasChild,hasSibling;
	int hasChild,hasSibling;
	id retval;

	NX_DURING
   		[super write:stream];
		hasChild = (child ? YES : NO);
		hasSibling = (sibling ? YES :NO);
		NXWriteTypes(stream,"ii",&hasVerknuepfung,&hasSound);
		NXWriteTypes(stream,"iiff",&hasChild,&hasSibling,&parentDistance,&border);
		NXWriteTypes(stream,"iiiiiff",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth,&biegFactor);
		NXWriteTypes(stream,"ii",&ending,&parentEnding);
		NXWriteColor(stream,fillColor);
		NXWriteColor(stream,outlineColor);
		NXWriteColor(stream,shadowColor);
		NXWriteObject(stream,textCell);
		NXWriteObject(stream,nodeDescription);
		NXWriteObjectReference(stream, parent);
		if(hasChild)
			NXWriteObject(stream, child);
		if(hasSibling)
			NXWriteObject(stream, sibling);
		if(hasVerknuepfung)
			NXWriteObject(stream,verknuepfung);
		if(hasSound)
			NXWriteObject(stream,geraeusch);
   		retval = self;
	NX_HANDLER
		retval = nil;
    NX_ENDHANDLER
	return retval;
}

- read:(NXTypedStream *)stream
{
	//BOOL hasChild,hasSibling;
	int hasChild,hasSibling;
	id retval;

	NX_DURING
		NXSetTypedStreamZone(stream, [self zone]);
   		[super read:stream];
		NXReadTypes(stream,"ii",&hasVerknuepfung,&hasSound);
		NXReadTypes(stream,"iiff", &hasChild,&hasSibling,&parentDistance,&border);
		if(NXTypedStreamClassVersion(stream, "Tree") == [Tree version]) 
			NXReadTypes(stream,"iiiiiff",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth,&biegFactor);
		else {
			NXReadTypes(stream,"iiiiif",&zipped,&shadow,&outline,&pathKind,&linkKind,&linewidth);
			biegFactor = 0.33;
		}	
		NXReadTypes(stream,"ii",&ending,&parentEnding);
		fillColor = NXReadColor(stream);
		outlineColor = NXReadColor(stream);
		shadowColor = NXReadColor(stream);
		textCell = NXReadObject(stream);
		nodeDescription = NXReadObject(stream);
		parent = NXReadObject(stream);
		if(hasChild)
			child = NXReadObject(stream);
		else
			child = nil;
		if(hasSibling)
			sibling = NXReadObject(stream);
		else
			sibling = nil;
		if(hasVerknuepfung)
			verknuepfung = NXReadObject(stream);
		if(hasSound)
			geraeusch = NXReadObject(stream);
		retval = self;
	NX_HANDLER
		retval = nil;
    NX_ENDHANDLER
   	return retval;
}

- awake
{
   	[super awake];
	contour.lower.head = contour.upper.head = NULL;
   	contour.lower.tail = contour.upper.tail = NULL;
	offset.x = offset.y = 0.0;
	pos.x = pos.y = 0.0; 
	gpath = [[UPath allocFromZone:[self zone]] init];
	deltaShadow.x = deltaShadow.y = 3;
	placer.rect[0] = placer.rect[1] = 6;
	placer.roundRect[0] = placer.roundRect[1] = 6;
	placer.roundRect[2] = 3;
	placer.ellipse[0] = 6;
	placer.ellipse[1] = 4;
	placer.rhomb[0] = 12;
	placer.rhomb[1] = 3;
	placer.hexagon[0] = 6;
	placer.hexagon[1] = 6;
	placer.hexagon[2] = 1;
	[self makePath]; 
	selected = NO;	
	updateLayout = YES;
	delegate = nil;
	fill = YES;
   	return self;
}

//*************************************************************************
// SearchableText support

- searchTreeFor:(const char *)pattern untilNode:endNode 
  reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(int *)positionS size:(int *)sizeS
{
	MiscString *labelS;
	id result;
	int resultPos;
	
	result = nil;
	resultPos = -1;
	labelS = [[MiscString alloc] initString:[self label]];
	if(regexpr)
		resultPos = [labelS spotOfRegex:pattern caseSensitive:cases length:sizeS];
	else
		resultPos = [labelS spotOfStr:pattern caseSensitive:cases];
	if(resultPos > -1){ 
		*positionS = resultPos;
		if(!regexpr)
			*sizeS = strlen(pattern);
		result = self;
	} else if(self == endNode)
		return nil;
	else {
		if(rev)
			result = [[self previousInDepth] searchTreeFor:pattern untilNode:endNode 
  				       reverse:rev regexpr:regexpr cases:cases position:positionS size:sizeS];
		else
			result = [[self nextInDepth] searchTreeFor:pattern untilNode:endNode 
  				       reverse:rev regexpr:regexpr cases:cases position:positionS size:sizeS];
	}
	[labelS free];
	return result;
}

- replaceTreeFor:(const char *)pattern with:(const char *)replacement untilNode:endNode 
  regexpr:(BOOL)regexpr cases:(BOOL)cases result:(int *)result
{
	MiscString *labelS;
	int nr;
	
	nr = 0;
	labelS = [[MiscString alloc] initString:[self label]];
	if(regexpr)
		nr = [labelS replaceEveryOccurrenceOfRegex:pattern with:replacement caseSensitive:cases];
	else
		nr = [labelS replaceEveryOccurrenceOf:pattern with:replacement caseSensitive:cases];
	if(nr > 0){
		[textCell setStringValue:[labelS stringValue]];
		[self changeText];
		[self setUpdateLayout:YES];
	}
	*result += nr;
	if(self != endNode)
		[[self nextInDepth] replaceTreeFor:pattern with:replacement untilNode:endNode 
  	 	 regexpr:regexpr cases:cases result:result];
	[labelS free];
	return self;
}
    
- nextInDepth
{
	Tree *r, *t;
	
	if(child)
		r = child;
	else if(sibling)
		r = sibling;
	else {
		t = self;
		while([t parent] && ![t sibling])
			t = [t parent];
		if([t sibling])	
			r = [t sibling];
		else
			r = t;
	}
	return r;
}

- previousInDepth
{
	Tree *r, *t;
	
	if(!parent)
	 	return [self lastInDepth];
	if([parent child] == self)
		return parent;
	else {
		r = [parent child];
		t = [r sibling];
		while(t != self){
			r = t;
			t = [t sibling];
		}
		return [r lastInDepth];
	}
}			

- lastInDepth
{
	Tree *r, *t;
	
	r = self;
	t = child;
	while(t){
		r = t;
		while(t){
			r = t;
			t = [t sibling];
		}
		t = [r child];
	}
	return r;
}

- (BOOL)replaceLabelSelectionWith:(const char *)replacement pos:(int)aPos size:(int)aSize
{
	MiscString *labelS, *pattern;
	int nr;
	BOOL result = NO;
	
	labelS = [[MiscString alloc] initString:[self label]];
	if(aPos + aSize > [labelS length])
		return result;
	pattern = [labelS midFrom:aPos to:(aPos + aSize - 1) fromZone:[self zone]];
	nr = [labelS replaceEveryOccurrenceOfString:pattern with:replacement caseSensitive:YES];
	if(nr > 0){
		[textCell setStringValue:[labelS stringValue]];
		[self changeText];
		[self setUpdateLayout:YES];
		result = YES;
	}
	[labelS free];
	return result;
}
		               
@end

@implementation Tree(PrivateMethods)

- attachParent:(NXCoord)h
{
    NXCoord x,y1,y2;

    x = border + parentDistance;
    y2 = (h - height) / 2 - border;
    y1 = y2 + height + 2 * border - h;
    [child setOffset:x + width :y1];  
    contour.upper.head = allocLine(width,0,allocLine(x,y1,contour.upper.head,[self zone]),[self zone]);
    contour.lower.head = allocLine(width,0,allocLine(x,y2,contour.lower.head,[self zone]),[self zone]);
    return self;
}

- layoutLeaf
{
	if(contour.lower.head)
		freePolyline(contour.lower.head);
	if(contour.upper.head)
		freePolyline(contour.upper.head);
    contour.upper.tail = allocLine(width + 2 * border,0,NULL,[self zone]);
    contour.upper.head = contour.upper.tail;
    contour.lower.tail = allocLine(0, - height - 2 * border,NULL,[self zone]);
    contour.lower.head = allocLine(width + 2 * border,0,contour.lower.tail,[self zone]);
    return self;
}

- (NXCoord)join
{
    Tree *c;
    NXCoord d,h,sum;
	Polygon dummy;

    c = child;
	if(contour.lower.head)
		freePolyline(contour.lower.head);
	if(contour.upper.head)
		freePolyline(contour.upper.head);
    if(c->contour.upper.head)
   		contour.upper.head = copyPolyline(c->contour.upper.head,&(contour.upper.tail),[self zone]);
	else
		contour.upper.head = contour.upper.tail = NULL;
	if(c->contour.lower.head)
   		contour.lower.head = copyPolyline(c->contour.lower.head,&(contour.lower.tail),[self zone]);
	else
		contour.lower.head = contour.lower.tail = NULL;
    sum = h = [c height] + 2 * [c border];
    c = [c sibling];
    while(c) {
		if(c->contour.upper.head)
   			dummy.upper.head = copyPolyline(c->contour.upper.head,&(dummy.upper.tail),[self zone]);
		else
			dummy.upper.head = dummy.upper.tail = NULL;
		if(c->contour.lower.head)
   			dummy.lower.head = copyPolyline(c->contour.lower.head,&(dummy.lower.tail),[self zone]);
		else
			dummy.lower.head = dummy.lower.tail = NULL;
        d = merge(&contour,&dummy,[self zone]);
        [c setOffset:0 :d + h];
        h = [c height] + 2 * [c border];
        sum += d + h;
        c = [c sibling];
    } 
    return sum;
}

- makePath
{
	float r;
	
	[self calcWidthHeight];
	switch(pathKind){
		case RECT_NODETYPE:
			[gpath moveto:pos.x :pos.y];
			[gpath rlineto:width :0.0];
			[gpath rlineto:0.0 :height];
			[gpath rlineto:-width :0.0];
			[gpath rlineto:0.0 :-height];
			break;
		case ROUNDRECT_NODETYPE:
			r = MIN(width / 3, height / 3);
			[gpath moveto:pos.x :pos.y + r];
			[gpath lineto:pos.x :pos.y + height - r];
			[gpath curveto:pos.x :pos.y + height - r + r * 0.5519
					:pos.x + r - r * 0.5519 :pos.y + height
					:pos.x + r :pos.y + height];
			[gpath lineto:pos.x + width - r :pos.y + height];
			[gpath curveto:pos.x + width - r + r * 0.5519 :pos.y + height
					:pos.x + width :pos.y + height - r + r * 0.5519
					:pos.x + width :pos.y + height - r];
			[gpath lineto:pos.x + width :pos.y + r];
			[gpath curveto:pos.x + width :pos.y + r - r * 0.5519
					:pos.x + width - r + r * 0.5519 :pos.y
					:pos.x + width - r :pos.y];
			[gpath lineto:pos.x + r :pos.y];
			[gpath curveto:pos.x + r - r * 0.5519 :pos.y
					:pos.x :pos.y + r - r * 0.5519  
					:pos.x :pos.y + r];
		/*	[gpath moveto:(pos.x + height / 3)  :pos.y];
			[gpath arct:(pos.x + width) :pos.y :(pos.x + width) :(pos.y + height) :height / 3];
			[gpath arct:(pos.x + width) :(pos.y + height) :pos.x :(pos.y + height) :height / 3];
			[gpath arct:pos.x :(pos.y + height) :pos.x :pos.y : height / 3];
			[gpath arct:pos.x :pos.y :(pos.x + width) :pos.y :height / 3];  */
			break;
		case ELLIPSE_NODETYPE:
			[gpath moveto:(pos.x + width) :(pos.y + height / 2)];
			[gpath curveto:(pos.x + width) :(pos.y + height / 2 * 0.445)
						:(pos.x + width / 2 * 1.555)  :pos.y
						:(pos.x + width / 2) :pos.y];
			[gpath curveto:(pos.x + width / 2 * 0.445) :pos.y
						:pos.x :(pos.y + height / 2 * 0.445)
						:pos.x :(pos.y + height / 2)];
			[gpath curveto:pos.x :(pos.y + height /2 * 1.555)
						:(pos.x + width / 2 * 0.445) :(pos.y + height)
						:(pos.x + width / 2) :(pos.y + height)];
			[gpath curveto:(pos.x + width / 2 * 1.555) :(pos.y + height)
						:(pos.x + width) :(pos.y + height / 2 * 1.555)
						:(pos.x + width) :(pos.y + height / 2)];
			break;
		case RHOMB_NODETYPE: 
			[gpath moveto:pos.x + width / 2 :pos.y];
			[gpath rlineto:width / 2 :height / 2];
			[gpath rlineto:-width / 2 :height / 2];
			[gpath rlineto:-width / 2 :-height / 2];
			[gpath rlineto:width / 2 :-height / 2];
			break;
		case HEXAGON_NODETYPE:
			[gpath moveto:pos.x + placer.hexagon[0] :pos.y];
			[gpath rlineto:(width - 2 * placer.hexagon[0]) :0];
			[gpath rlineto:placer.hexagon[0] :height /2];
			[gpath rlineto:-placer.hexagon[0] :height /2];
			[gpath rlineto:(- width + 2 * placer.hexagon[0]) :0];
			[gpath rlineto:-placer.hexagon[0] :-height / 2];
			[gpath rlineto:placer.hexagon[0] :-height / 2];
			break;
		default:
			break;	
	} 
	return self;
}

- changeText
{
	[self calcWidthHeight];
	[self fillGPathParams];
	gpath->bbox[2] = gpath->bbox[0] + width;
	gpath->bbox[3] = gpath->bbox[1] + height; 
	return self;
}

- changePos
{
	NXPoint delta;

	[self fillGPathParams];
	switch(pathKind){
		case RECT_NODETYPE:
			delta.x = gpath->params[0] - gpath->bbox[0];
			delta.y = gpath->params[1] - gpath->bbox[1];
			break;
		case ROUNDRECT_NODETYPE:
			delta.x = gpath->params[0] - gpath->bbox[0];
			delta.y = gpath->params[23] - gpath->bbox[1];
			break;
		case ELLIPSE_NODETYPE:
			delta.x = gpath->params[10] - gpath->bbox[0];
			delta.y = gpath->params[5] - gpath->bbox[1];
			break;
		case RHOMB_NODETYPE:
			delta.x = gpath->params[0] - gpath->bbox[0] - width / 2;
			delta.y = gpath->params[1] - gpath->bbox[1];
			break;
		case HEXAGON_NODETYPE:
			delta.x = gpath->params[0] - gpath->bbox[0] - placer.hexagon[0];
			delta.y = gpath->params[1] - gpath->bbox[1];
			break;
		default:
			break;	
	}
	gpath->bbox[0] += delta.x;
   	gpath->bbox[1] += delta.y;
    gpath->bbox[2] += delta.x;
    gpath->bbox[3] += delta.y;
	return self;
}

- fillGPathParams
{
	float r;
	
	switch(pathKind){
		case RECT_NODETYPE:
			gpath->params[0] = pos.x;
			gpath->params[1] = pos.y;
			gpath->params[2] = width;
			gpath->params[3] = 0.0;
			gpath->params[4] = 0.0;
			gpath->params[5] = height;
			gpath->params[6] = -width;
			gpath->params[7] = 0.0;
			gpath->params[8] = 0.0;
			gpath->params[9] = -height;
			break;
		case ROUNDRECT_NODETYPE:
			r = MIN(width / 3, height / 3);
			gpath->params[0] = pos.x;
			gpath->params[1] = pos.y + r;
			gpath->params[2] = pos.x;
			gpath->params[3] = pos.y + height - r;
			gpath->params[4] = pos.x;
			gpath->params[5] = pos.y + height - r + r * 0.5519;
			gpath->params[6] = pos.x + r - r * 0.5519;
			gpath->params[7] = pos.y + height;
			gpath->params[8] = pos.x + r;
			gpath->params[9] = pos.y + height;
			gpath->params[10] = pos.x + width - r;
			gpath->params[11] = pos.y + height;
			gpath->params[12] = pos.x + width - r + r * 0.5519;
			gpath->params[13] = pos.y + height;
			gpath->params[14] = pos.x + width;
			gpath->params[15] = pos.y + height - r + r * 0.5519;
			gpath->params[16] = pos.x + width;
			gpath->params[17] = pos.y + height - r;
			gpath->params[18] = pos.x + width;
			gpath->params[19] = pos.y + r;
			gpath->params[20] = pos.x + width;
			gpath->params[21] = pos.y + r - r * 0.5519;
			gpath->params[22] = pos.x + width - r + r * 0.5519;
			gpath->params[23] = pos.y;
			gpath->params[24] = pos.x + width - r;
			gpath->params[25] = pos.y;
			gpath->params[26] = pos.x + r;
			gpath->params[27] = pos.y;
			gpath->params[28] = pos.x + r - r * 0.5519;
			gpath->params[29] = pos.y;
			gpath->params[30] = pos.x;
			gpath->params[31] = pos.y + r - r * 0.5519;
			gpath->params[32] = pos.x;
			gpath->params[33] = pos.y + r;
		/*	gpath->params[0] = pos.x + height / 3;
			gpath->params[1] = pos.y;
			gpath->params[2] = pos.x + width;
			gpath->params[3] = pos.y;
			gpath->params[4] = pos.x + width;
			gpath->params[5] = pos.y + height;
			gpath->params[6] = height / 3;
			gpath->params[7] = pos.x + width;
			gpath->params[8] = pos.y + height;
			gpath->params[9] = pos.x;
			gpath->params[10] = pos.y + height;
			gpath->params[11] = height / 3;
			gpath->params[12] = pos.x;
			gpath->params[13] = pos.y + height;
			gpath->params[14] = pos.x;
			gpath->params[15] = pos.y;
			gpath->params[16] = height / 3;
			gpath->params[17] = pos.x;
			gpath->params[18] = pos.y;
			gpath->params[19] = pos.x + width;
			gpath->params[20] = pos.y;
			gpath->params[21] = height / 3; */
			break;
		case ELLIPSE_NODETYPE:
			gpath->params[0] = pos.x + width;
			gpath->params[1] = pos.y + height / 2;
			gpath->params[2] = pos.x + width;
			gpath->params[3] = pos.y + height / 2 * 0.445;
			gpath->params[4] = pos.x + width / 2 * 1.555;
			gpath->params[5] = pos.y;
			gpath->params[6] = pos.x + width / 2; 
			gpath->params[7] = pos.y;
			gpath->params[8] = pos.x + width / 2 * 0.445;
			gpath->params[9] = pos.y;
			gpath->params[10] = pos.x;
			gpath->params[11] = pos.y + height / 2 * 0.445;
			gpath->params[12] = pos.x;
			gpath->params[13] = pos.y + height / 2;
			gpath->params[14] = pos.x;
			gpath->params[15] = pos.y + height /2 * 1.555;
			gpath->params[16] = pos.x + width / 2 * 0.445;
			gpath->params[17] = pos.y + height;
			gpath->params[18] = pos.x + width / 2;
			gpath->params[19] = pos.y + height;
			gpath->params[20] = pos.x + width / 2 * 1.555;
			gpath->params[21] = pos.y + height;
			gpath->params[22] = pos.x + width;
			gpath->params[23] = pos.y + height / 2 * 1.555;
			gpath->params[24] = pos.x + width;
			gpath->params[25] = pos.y + height / 2;
			break;
		case RHOMB_NODETYPE:
			gpath->params[0] = pos.x + width / 2;
			gpath->params[1] = pos.y;
			gpath->params[2] = width / 2;
			gpath->params[3] = height / 2;
			gpath->params[4] = -width / 2;
			gpath->params[5] = height / 2;
			gpath->params[6] = -width / 2;
			gpath->params[7] = -height / 2;
			gpath->params[8] =width / 2;
			gpath->params[9] = -height / 2;
			break;
		case HEXAGON_NODETYPE:
			gpath->params[0] = pos.x + placer.hexagon[0];
			gpath->params[1] = pos.y;
			gpath->params[2] = width - 2 * placer.hexagon[0];
			gpath->params[3] = 0;
			gpath->params[4] = placer.hexagon[0];
			gpath->params[5] = height / 2;
			gpath->params[6] = -placer.hexagon[0];
			gpath->params[7] = height / 2;
			gpath->params[8] =-width + 2 * placer.hexagon[0];
			gpath->params[9] = 0;
			gpath->params[10] =-placer.hexagon[0];
			gpath->params[11] = -height / 2;
			gpath->params[12] = placer.hexagon[0];
			gpath->params[13] = -height / 2;
			break;
		default:
			break;	
	}
	return self;
}

- calcWidthHeight
{
	NXCoord v1,v2;
	NXSize textSize;

	[textCell calcCellSize:&textSize];
	switch(pathKind){
		case RECT_NODETYPE:
			width = textSize.width + 2 * placer.rect[0];
			height = textSize.height + 2 * placer.rect[1];
			break;
		case ROUNDRECT_NODETYPE:
			width = textSize.width + 2 * placer.roundRect[0];
			height = textSize.height + 2 * placer.roundRect[1];
			break;
		case ELLIPSE_NODETYPE:
			height = textSize.height + 2 * placer.ellipse[0];
			v1 = placer.ellipse[0] - placer.ellipse[1];
			v2 = placer.ellipse[0] + height + placer.ellipse[1];
			width = (textSize.width / 2 + placer.ellipse[1]) * height / sqrt(v1 * v2);
			break;
		case RHOMB_NODETYPE:
			height = textSize.height + 2 * placer.rhomb[0];
			v1 = placer.rhomb[0] - placer.rhomb[1];
			width = (textSize.width / 2 + placer.rhomb[1]) * height / v1;
			break;
		case HEXAGON_NODETYPE:
			width = textSize.width + 2 * placer.hexagon[0];
			height = textSize.height + 2 * placer.hexagon[1];
			break;
		default:
			break;
	}
	deltaText.x = (width - textSize.width) / 2;
	deltaText.y = (height - textSize.height) / 2;
	deltaLink.x = width / 2;
	deltaLink.y = height / 2;
	NXSetRect(&textCellFrame, 0, 0, textSize.width, textSize.height);
	return self;
}

- internalCopyFromZone:(NXZone *)zone parent:aParent
{
   	Tree *theCopy;

   	theCopy = [super copyFromZone:zone];
	theCopy->contour.upper.head = contour.upper.tail = NULL;
	theCopy->contour.lower.head = contour.lower.tail = NULL;
	theCopy->updateLayout = YES;
	theCopy->parent = aParent;
	if(child)
		theCopy->child = [child internalCopyFromZone:zone parent:theCopy];
	else
		theCopy->child = nil;
	if(sibling)
		theCopy->sibling = [sibling internalCopyFromZone:zone parent:aParent];
	else
		theCopy->sibling =	nil;
	theCopy->selected = NO;
	theCopy->gpath = [gpath copyFromZone:zone];
	theCopy->nodeDescription = [nodeDescription copyFromZone:zone];;
	theCopy->delegate = nil;
	if(verknuepfung)
		theCopy->verknuepfung = [verknuepfung copyFromZone:zone];
	else
		theCopy->verknuepfung = nil;
	theCopy->textCell = [textCell copyFromZone:zone];
   	return theCopy;
}

- calcIntersection:(NXPoint *)ip angle:(float *)alpha toPoint:(const NXPoint *)aPoint
{
	NXPoint middle,l1,l2;
	float s,xm,ym,x0,y0,r,ausdruck,a,b;
	
	middle.x = pos.x + width / 2;
	middle.y = pos.y + height / 2;
	if(aPoint->y == middle.y){
		*alpha = 0;
		ip->y = middle.y;
		if(aPoint->x > middle.x)
			ip->x = pos.x + width;
		else
			ip->x = pos.x;
		return self;
	}
	if(aPoint->x == middle.x){
		ip->x = middle.x;
		if(aPoint->y > middle.y){
			ip->y = pos.y + height;
			*alpha = -90;
		} else {
			ip->y = pos.y;
			*alpha = 90;
		}
		return self;
	}
	s = (aPoint->y - middle.y) / (aPoint->x - middle.x);
	*alpha = 57.29577951 * atan(s);
	if(aPoint->y > middle.y){
		if(aPoint->x > middle.x){ 						// 4. quadrant
			switch(pathKind){
				case RECT_NODETYPE:
					l1.x = middle.x;
					l1.y = l2.y = pos.y + height;
					l2.x = pos.x + width;
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						ip->x = l2.x;
						ip->y = middle.y + s * (ip->x - middle.x);
					}
				break;
				case ROUNDRECT_NODETYPE:
					l1.x = middle.x;
					l1.y = pos.y + height;
					l2.x = pos.x + width - height / 3;
					l2.y = pos.y + height;
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						l1.x = l2.x = pos.x + width;
						l1.y = middle.y;
						l2.y = pos.y + height - height / 3;
						if(straddles(&l1, &l2, &middle, aPoint)){
							ip->x = l2.x;
							ip->y = middle.y + s * (ip->x - middle.x);	
						} else {
							r = height / 3;
							x0 = middle.x;
							y0 = middle.y;
							xm = pos.x + width - r;
							ym = pos.y + height - r;
							ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 
          								2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - 
          								pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
							ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
							ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
							if(ip->y < ym){
								ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
								ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
							}
						}
					}	 
				break;
				case ELLIPSE_NODETYPE:
					x0 = middle.x;
					y0 = middle.y;
					a = width / 2;
					b = height / 2;
					ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
					ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
					if(ip->y < y0){
						ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
						ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
					}
				break;
				case RHOMB_NODETYPE:
					l1.x = middle.x;
					l1.y = pos.y + height;
					l2.x = pos.x + width;
					l2.y = middle.y;
					ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
					ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
					ip->y = middle.y + s * (ip->x - middle.x);
				break;
				case HEXAGON_NODETYPE:
					l1.x = middle.x;
					l1.y = l2.y = pos.y + height;
					l2.x = pos.x + width - placer.hexagon[0];
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						l1.x = pos.x + width;
						l1.y = middle.y;
						ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
						ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
						ip->y = middle.y + s * (ip->x - middle.x);
					}
				break;
				default:
				break;
			}
		} else { 										// 3. quadrant
			switch(pathKind){
				case RECT_NODETYPE:
					l1.x = middle.x;
					l1.y = l2.y = pos.y + height;
					l2.x = pos.x;
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						ip->x = l2.x;
						ip->y = middle.y + s * (ip->x - middle.x);
					}
				break;
				case ROUNDRECT_NODETYPE:
					l1.x = middle.x;
					l1.y = pos.y + height;
					l2.x = pos.x + height / 3;
					l2.y = pos.y + height;
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						l1.x = l2.x = pos.x;
						l1.y = middle.y;
						l2.y = pos.y + height - height / 3;
						if(straddles(&l1, &l2, &middle, aPoint)){
							ip->x = l2.x;
							ip->y = middle.y + s * (ip->x - middle.x);	
						} else {
							r = height / 3;
							x0 = middle.x;
							y0 = middle.y;
							xm = pos.x + r;
							ym = pos.y + height - r;
							ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 
          								2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - 
          								pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
							ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
							ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
							if(ip->y < ym){
								ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
								ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
							}
						}
					}
				break;
				case ELLIPSE_NODETYPE:
					x0 = middle.x;
					y0 = middle.y;
					a = width / 2;
					b = height / 2;
					ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
					ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
					if(ip->y < y0){
						ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
						ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
					}
				break;
				case RHOMB_NODETYPE:
					l1.x = middle.x;
					l1.y = pos.y + height;
					l2.x = pos.x;
					l2.y = middle.y;
					ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
					ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
					ip->y = middle.y + s * (ip->x - middle.x);
				break;
				case HEXAGON_NODETYPE:
					l1.x = middle.x;
					l1.y = l2.y = pos.y + height;
					l2.x = pos.x + placer.hexagon[0];
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						l1.x = pos.x;
						l1.y = middle.y;
						ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
						ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
						ip->y = middle.y + s * (ip->x - middle.x);
					}
				break;
				default:
				break;
			}
		}
	} else {
		if(aPoint->x > middle.x){ 						// 1. quadrant
			switch(pathKind){
				case RECT_NODETYPE:
					l1.x = middle.x;
					l1.y = l2.y = pos.y;
					l2.x = pos.x + width;
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						ip->x = l2.x;
						ip->y = middle.y + s * (ip->x - middle.x);
					}
				break;
				case ROUNDRECT_NODETYPE:
					l1.x = middle.x;
					l1.y = pos.y;
					l2.x = pos.x + width - height / 3;
					l2.y = pos.y;
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						l1.x = l2.x = pos.x + width;
						l1.y = middle.y;
						l2.y = pos.y + height / 3;
						if(straddles(&l1, &l2, &middle, aPoint)){
							ip->x = l2.x;
							ip->y = middle.y + s * (ip->x - middle.x);	
						} else {
							r = height / 3;
							x0 = middle.x;
							y0 = middle.y;
							xm = pos.x + width - r;
							ym = pos.y + r;
							ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 
          								2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - 
          								pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
							ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
							ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
							if(ip->y > ym){
								ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
								ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
							}
						}
					}
				break;
				case ELLIPSE_NODETYPE:
					x0 = middle.x;
					y0 = middle.y;
					a = width / 2;
					b = height / 2;
					ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
					ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
					if(ip->y > y0){
						ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
						ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
					}
				break;
				case RHOMB_NODETYPE:
					l1.x = middle.x;
					l1.y = pos.y;
					l2.x = pos.x + width;
					l2.y = middle.y;
					ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
					ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
					ip->y = middle.y + s * (ip->x - middle.x);
				break;
				case HEXAGON_NODETYPE:
					l1.x = middle.x;
					l1.y = l2.y = pos.y;
					l2.x = pos.x + width - placer.hexagon[0];
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						l1.x = pos.x + width;
						l1.y = middle.y;
						ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
						ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
						ip->y = middle.y + s * (ip->x - middle.x);
					}
				break;
				default:
				break;
			}
		} else { 										// 2. quadrant
			switch(pathKind){
				case RECT_NODETYPE:
					l1.x = middle.x;
					l1.y = l2.y = pos.y;
					l2.x = pos.x;
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						ip->x = l2.x;
						ip->y = middle.y + s * (ip->x - middle.x);
					}
				break;
				case ROUNDRECT_NODETYPE:
					l1.x = middle.x;
					l1.y = pos.y;
					l2.x = pos.x + height / 3;
					l2.y = pos.y;
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						l1.x = l2.x = pos.x;
						l1.y = middle.y;
						l2.y = pos.y + height / 3;
						if(straddles(&l1, &l2, &middle, aPoint)){
							ip->x = l2.x;
							ip->y = middle.y + s * (ip->x - middle.x);	
						} else {
							r = height / 3;
							x0 = middle.x;
							y0 = middle.y;
							xm = pos.x + r;
							ym = pos.y + r;
							ausdruck = (pow(r,2) + pow(r,2)*pow(s,2) - pow(s,2)*pow(xm,2) + 
          								2*pow(s,2)*xm*x0 - pow(s,2)*pow(x0,2) + 2*s*xm*ym - 2*s*x0*ym - 
          								pow(ym,2) - 2*s*xm*y0 + 2*s*x0*y0 + 2*ym*y0 - pow(y0,2));
							ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 + sqrt(ausdruck))/(1 + pow(s,2));
							ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 + s*sqrt(ausdruck))/(1 + pow(s,2));
							if(ip->y > ym){
								ip->x = (xm + pow(s,2)*x0 + s*ym - s*y0 - sqrt(ausdruck))/(1 + pow(s,2));
								ip->y = (s*xm - s*x0 + pow(s,2)*ym + y0 - s*sqrt(ausdruck))/(1 + pow(s,2));
							}
						}
					}
				break;
				case ELLIPSE_NODETYPE:
					x0 = middle.x;
					y0 = middle.y;
					a = width / 2;
					b = height / 2;
					ip->x = a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + x0;
					ip->y = (2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
					if(ip->y > y0){
						ip->x = -(a*b/sqrt(pow(b,2) + pow(a,2)*pow(s,2))) + x0;
						ip->y = (-2*a*b*s/sqrt(pow(b,2) + pow(a,2)*pow(s,2)) + 2*y0)/2;
					}
				break;
				case RHOMB_NODETYPE:
					l1.x = middle.x;
					l1.y = pos.y;
					l2.x = pos.x;
					l2.y = middle.y;
					ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
					ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
					ip->y = middle.y + s * (ip->x - middle.x);
				break;
				case HEXAGON_NODETYPE:
					l1.x = middle.x;
					l1.y = l2.y = pos.y;
					l2.x = pos.x + placer.hexagon[0];
					if(straddles(&l1, &l2, &middle, aPoint)){
						ip->y = l1.y;
						ip->x = middle.x + (ip->y - middle.y) / s;
					} else {
						l1.x = pos.x;
						l1.y = middle.y;
						ausdruck = (l2.y - l1.y) / (l2.x - l1.x);
						ip->x = (middle.y + ausdruck * l1.x - s * middle.x - l1.y) / (ausdruck - s);
						ip->y = middle.y + s * (ip->x - middle.x);
					}
				break;
				default:
				break;
			}
		}
	}
	return self;
}
		
@end

//************************************************************************
//  static c functions implementation

static void setRectFromPolygon(NXRect *rect, Polygon *aPol, const NXPoint *ipu, const NXPoint *ipl,
								NXCoord *low, NXCoord *loh)
{
	NXCoord minX, minY, maxX, maxY, x, y;
	Polyline *dummy;

	minX = minY = 9999;
	maxX = maxY = -9999;
	dummy = aPol->upper.head;
	x = ipu->x;
	y = ipu->y;
	minX = MIN(minX, x);
	minY = MIN(minY, y);
	maxX = MAX(maxX, x);
	maxY = MAX(maxY, y);
	while(dummy){
		x += dummy->dx;
		y += dummy->dy;
		minX = MIN(minX, x);
		minY = MIN(minY, y);
		maxX = MAX(maxX, x);
		maxY = MAX(maxY, y);
		dummy = dummy->link;
	} 
	dummy = aPol->lower.head;
	x = ipl->x;
	y = ipl->y;
	minX = MIN(minX, x);
	minY = MIN(minY, y);
	maxX = MAX(maxX, x);
	maxY = MAX(maxY, y);
	while(dummy){
		x += dummy->dx;
		y += dummy->dy;
		minX = MIN(minX, x);
		minY = MIN(minY, y);
		maxX = MAX(maxX, x);
		maxY = MAX(maxY, y);
		dummy = dummy->link;
	}
	NXSetRect(rect, minX, minY, maxX - minX, maxY - minY);
	*loh = ipu->y - minY;
	*low = ipu->x - minX;
}

static NXCoord merge(Polygon *c1,Polygon *c2, NXZone *zone)
{
    NXCoord x,y,total,d;
    Polyline *lower,*upper,*b;

    x = y = total = 0;
    upper = c1->lower.head;
    lower = c2->upper.head;
    while(lower && upper){
        d = offset(x,y,lower->dx,lower->dy,upper->dx,upper->dy);
        y += d;
		total += d;
        if(x + lower->dx <= upper->dx){
               y += lower->dy;
               x += lower->dx;
               lower = lower->link;
        } else {
               y -= upper->dy;
               x -= upper->dx;
               upper = upper->link;
		}  
    }
    if(lower){
        b = bridge(c1->upper.tail,lower,0,0,x,y,zone);
        c1->upper.tail = (b->link) ? c2->upper.tail : b;
        c1->lower.tail = c2->lower.tail;
		partialFreePolyline(c2->upper.head, lower->link);
		freePolyline(c1->lower.head);
    } else {
        b = bridge(c2->lower.tail,upper,x,y,0,0,zone);
        if(!b->link)
            c1->lower.tail = b;
		partialFreePolyline(c1->lower.head,upper->link);
		freePolyline(c2->upper.head);	
    }
    c1->lower.head = c2->lower.head;
    return total;
}

static NXCoord offset(NXCoord p1,NXCoord p2,NXCoord a1,NXCoord a2,
                      NXCoord b1,NXCoord b2)
{
    NXCoord d,s,t;

    if(b1 <= p1 || p1 + a1 <= 0)
         return 0;
    t = b1 * a2 - a1 * b2;
    if(t > 0){
        if(p1 < 0)
            s = p1 * a2,d = s / a1 - p2;
        else if(p1 > 0)
            s = p1 * b2,d = s / b1 - p2;
        else
            d = -p2;
    } else if(b1 < p1 + a1)
        s = (b1 - p1) * a2,d = b2 - (p2 + s / a1);
    else if(b1 > p1 + a1)
        s = (a1 + p1) * b2, d = s / b1 - (p2 + a2);
    else
        d = b2 - (p2 + a2);
    return MAX(0,d);
}

static Polyline *bridge(Polyline *line1,Polyline *line2,NXCoord x1,NXCoord y1,
                       NXCoord x2,NXCoord y2, NXZone *zone)
{
    NXCoord dx,dy,s;
    Polyline *r;
   
    dx = x2 + line2->dx - x1;
    if(line2->dx == 0)
       dy = line2->dy;
    else
       s = dx * line2->dy,dy = s / line2->dx;
    r = allocLine(dx,dy,line2->link,zone);
    line1->link = allocLine(0,y2 + line2->dy -dy - y1,r,zone);
    return r;
}

static Polyline *allocLine(NXCoord aX,NXCoord aY,Polyline *aLink, NXZone *zone)
{
    Polyline *theAlloced;

    theAlloced = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline));
    theAlloced->dx = aX;
    theAlloced->dy = aY;
    theAlloced->link = aLink; 
    return theAlloced;
}

static Polyline *copyPolyline(Polyline *aSource,Polyline **aTail, NXZone *zone)
{
    Polyline *aLine,*l,*result;
    
    l = aSource;
    aLine = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline));
    aLine->dx = l->dx;
    aLine->dy = l->dy;
    aLine->link = NULL; 
    result = aLine;
    l = l->link;
    while(l){
        aLine->link = (Polyline *)NXZoneMalloc(zone,sizeof(Polyline));
        aLine = aLine->link;
        aLine->dx = l->dx;
        aLine->dy = l->dy;
        aLine->link = NULL;
        l = l->link;
    }
    *aTail = aLine;
    return result;
}

static void freePolyline(Polyline *aLine)
{
	Polyline *index,*trailer;
	
	trailer = aLine;
	index = aLine;
	while(index){
		index = index->link;
		NX_FREE(trailer);
		trailer = index;
	}
}

static void partialFreePolyline(Polyline *aLine, Polyline *toEnd)
{
	Polyline *index,*trailer;
	
	trailer = aLine;
	index = aLine;
	while(index && index != toEnd){
		index = index->link;
		NX_FREE(trailer);
		trailer = index;
	}
}

static BOOL straddles(const NXPoint *p1, const NXPoint *p2, const NXPoint *p3, const NXPoint *p4)
{
	float crossP1, crossP2;
	
	crossP1 = (p3->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (p3->y - p1->y); 
	crossP2 = (p4->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (p4->y - p1->y);
	if((crossP1 > 0 && crossP2 < 0) || (crossP1 < 0 && crossP2 > 0)){
		crossP1 = (p1->x - p3->x) * (p4->y - p3->y) - (p4->x - p3->x) * (p1->y - p3->y); 
		crossP2 = (p2->x - p3->x) * (p4->y - p3->y) - (p4->x - p3->x) * (p2->y - p3->y);
		if((crossP1 > 0 && crossP2 < 0) || (crossP1 < 0 && crossP2 > 0))
			return YES;
		else
			return NO;
	} else
		return NO;
} 
	
	
	
	
	
	